#include "CAnimals.h"
#include <stdio.h>

CAnimalsFactory AnimalsFactory;

CAnimalRoot AnimalRoot;

CVarietyFactory VarietyFactory;


//////////////////////////////////////////////////////////////////
//  CAnimals
//////////////////////////////////////////////////////////////////

CAnimals::CAnimals() : m_nCnt(0), m_lpName(nullptr)
{
    AnimalsFactory.LockServer(TRUE);
}

CAnimals::~CAnimals()
{
    AnimalsFactory.LockServer(FALSE);
}

HRESULT __stdcall CAnimals::SetName(IN LPOLESTR lpName)
{
    if (!lpName) return S_FALSE;

    size_t stLen;

    if (m_lpName)
    {
        delete[] m_lpName;
        m_lpName = nullptr;
    }

    stLen = _tcslen(lpName) + sizeof(TCHAR);
    m_lpName = new TCHAR[stLen];
    if (m_lpName)
    {
        RtlZeroMemory(m_lpName, stLen * sizeof(TCHAR));
        RtlCopyMemory(m_lpName, lpName, stLen * sizeof(TCHAR));
    }

    if (m_lpName)
        return S_OK;
    else
        return S_FALSE;
}

HRESULT __stdcall CAnimals::Woof(IN const unsigned char* lpString)
{
    if (m_lpName)
        _tprintf(m_lpName);
    else
        printf("Dog");

    printf(": ");

    if (lpString)
    {
        printf((char*)lpString);
    }
    return S_OK;
}

HRESULT __stdcall CAnimals::Meow(IN const unsigned char* lpString)
{
    if (m_lpName)
        _tprintf(m_lpName);
    else
        printf("Cat");

    printf(": ");

    if (lpString)
    {
        printf((char*)lpString);
    }
    return S_OK;
}

HRESULT __stdcall CAnimals::Oink(IN const unsigned char* lpString)
{
    if (m_lpName)
        _tprintf(m_lpName);
    else
        printf("Pig");

    printf(": ");

    if (lpString)
    {
        printf((char*)lpString);
    }
    return S_OK;
}

HRESULT __stdcall CAnimals::ParseDisplayName(IN IBindCtx* pbc, IN LPOLESTR pszDisplayName, OUT ULONG* pchEaten, OUT IMoniker** ppmkOut)
{
    HRESULT hResult;

    hResult = CreateItemMoniker(OLESTR("!"), pszDisplayName + 1, ppmkOut);
    if (pchEaten)
    {
        if (hResult == S_OK)
        {
            *pchEaten = _tcslen(pszDisplayName);
        }
        else
        {
            *pchEaten = 0;
        }
    }

    return hResult;
}

HRESULT __stdcall CAnimals::EnumObjects(IN DWORD grfFlags, OUT IEnumUnknown** ppenum)
{
    return S_OK;
}

HRESULT __stdcall CAnimals::LockContainer(IN BOOL fLock)
{
    return S_OK;
}

HRESULT __stdcall CAnimals::GetObject(IN LPOLESTR pszItem, IN DWORD dwSpeedNeeded, IN IBindCtx* pbc, IN REFIID riid, OUT void** ppvObject)
{
    IUnknown* lpIUnknown;
    ISetName* lpISetName;

    HRESULT hResult = CoCreateInstance(CLSID_ANIMAL_VARIETY, nullptr, CLSCTX_INPROC_SERVER, riid, ppvObject);
    if (hResult == S_OK)
    {
        lpIUnknown = (IUnknown*)*ppvObject;
        lpIUnknown->QueryInterface(IID_ISetName, (void**)&lpISetName);
        lpISetName->SetName(pszItem);
        lpISetName->Release();
    }
    return hResult;
}

HRESULT __stdcall CAnimals::GetObjectStorage(IN LPOLESTR pszItem, IN IBindCtx* pbc, IN REFIID riid, OUT void** ppvStorage)
{
    return S_OK;
}

HRESULT __stdcall CAnimals::IsRunning(IN LPOLESTR pszItem)
{
    return S_OK;
}


//////////////////////////////////////////////////////////////////
//  CVariety
//////////////////////////////////////////////////////////////////

CVariety::CVariety() : m_lpName(nullptr)
{
}

CVariety::~CVariety()
{
}

HRESULT __stdcall CVariety::SetName(IN LPOLESTR lpName)
{
    if (!lpName) return S_FALSE;

    size_t stLen;

    if (m_lpName)
    {
        delete[] m_lpName;
        m_lpName = nullptr;
    }

    stLen = _tcslen(lpName) + sizeof(TCHAR);
    m_lpName = new TCHAR[stLen];
    if (m_lpName)
    {
        RtlZeroMemory(m_lpName, stLen * sizeof(TCHAR));
        RtlCopyMemory(m_lpName, lpName, stLen * sizeof(TCHAR));
    }

    if (m_lpName)
        return S_OK;
    else
        return S_FALSE;
}

HRESULT __stdcall CVariety::Show()
{
    printf("Variety: ");
    if (m_lpName)
    {
        _tprintf(m_lpName);
    }
    printf("\r\n");
    return S_OK;
}


//////////////////////////////////////////////////////////////////
//  CVarietyFactory
//////////////////////////////////////////////////////////////////

CVarietyFactory::CVarietyFactory()
{
}

CVarietyFactory::~CVarietyFactory()
{
}

HRESULT __stdcall CVarietyFactory::CreateInstance(
    IN OPTIONAL IUnknown* pUnkOuter, 
    IN REFIID riid, 
    OUT void** ppvObject
)
{
    CVariety* lpVariety = new CVariety;
    return lpVariety->QueryInterface(riid, ppvObject);
}

HRESULT __stdcall CVarietyFactory::LockServer(IN BOOL fLock)
{
    return S_OK;
}


//////////////////////////////////////////////////////////////////
//  CAnimalsFactory
//////////////////////////////////////////////////////////////////

CAnimalsFactory::CAnimalsFactory()
{
}

CAnimalsFactory::~CAnimalsFactory()
{
}

HRESULT __stdcall CAnimalsFactory::QueryInterface(REFIID riid, void** ppvObject)
{
    if (!ppvObject)
        return E_POINTER;

    *ppvObject = nullptr;

    if (IID_IClassFactory == riid)
    {
        AddRef();
        *ppvObject = dynamic_cast<IClassFactory*>(this);
    }
    else if (IID_IOleItemContainer == riid)
    {
        AddRef();
        *ppvObject = dynamic_cast<IOleItemContainer*>(this);
    }
    else if (IID_IParseDisplayName == riid)
    {
        AddRef();
        *ppvObject = dynamic_cast<IParseDisplayName*>(this);
    }
    else if (IID_IUnknown == riid)
    {
        AddRef();
        *ppvObject = this;
    }
    else
    {
        CAnimals* lpAnimals = new CAnimals;
        return lpAnimals->QueryInterface(riid, ppvObject);
    }

    if (*ppvObject == nullptr)
        return S_FALSE;

    return S_OK;
}

ULONG __stdcall CAnimalsFactory::AddRef()
{
    return 1;
}

ULONG __stdcall CAnimalsFactory::Release()
{
    return 0;
}

HRESULT __stdcall CAnimalsFactory::CreateInstance(IN OPTIONAL IUnknown* pUnkOuter, IN REFIID riid, OUT void** ppvObject)
{
    CAnimals* lpAnimals = new CAnimals;
    return lpAnimals->QueryInterface(riid, ppvObject);
}

HRESULT __stdcall CAnimalsFactory::LockServer(IN BOOL fLock)
{
    if (fLock)
    {
        InterlockedIncrement(&m_nLockTick);
    }
    else
    {
        if (m_nLockTick > 0)
            InterlockedDecrement(&m_nLockTick);
    }

    return S_OK;
}

HRESULT __stdcall CAnimalsFactory::ParseDisplayName(
    IN IBindCtx* pbc, 
    IN LPOLESTR pszDisplayName, 
    OUT ULONG* pchEaten, 
    OUT IMoniker** ppmkOut
)
{
    HRESULT hResult;

    hResult = CreateItemMoniker(OLESTR("!"), pszDisplayName + 1, ppmkOut);
    if (pchEaten)
    {
        if (hResult == S_OK)
        {
            *pchEaten = _tcslen(pszDisplayName);
        }
        else
        {
            *pchEaten = 0;
        }
    }

    return hResult;
}

HRESULT __stdcall CAnimalsFactory::EnumObjects(IN DWORD grfFlags, OUT IEnumUnknown** ppenum)
{
    return S_OK;
}

HRESULT __stdcall CAnimalsFactory::LockContainer(IN BOOL fLock)
{
    return S_OK;
}

HRESULT __stdcall CAnimalsFactory::GetObject(
    IN LPOLESTR pszItem, 
    IN DWORD dwSpeedNeeded, 
    IN IBindCtx* pbc, 
    IN REFIID riid, 
    OUT void** ppvObject
)
{
    IUnknown* lpIUnknown;
    ISetName* lpISetName;

    HRESULT hResult = CoCreateInstance(CLSID_ANIMAL_VARIETY, nullptr, CLSCTX_INPROC_SERVER, riid, ppvObject);
    if (hResult == S_OK)
    {
        lpIUnknown = (IUnknown*)*ppvObject;
        lpIUnknown->QueryInterface(IID_ISetName, (void**)&lpISetName);
        lpISetName->SetName(pszItem);
        lpISetName->Release();
    }
    return hResult;
}

HRESULT __stdcall CAnimalsFactory::GetObjectStorage(
    IN LPOLESTR pszItem, 
    IN IBindCtx* pbc, 
    IN REFIID riid, 
    OUT void** ppvStorage
)
{
    return S_OK;
}

HRESULT __stdcall CAnimalsFactory::IsRunning(IN LPOLESTR pszItem)
{
    return S_OK;
}


//////////////////////////////////////////////////////////////////
//  CAnimalRoot
//////////////////////////////////////////////////////////////////

CAnimalRoot::CAnimalRoot()
{
}

CAnimalRoot::~CAnimalRoot()
{
}

HRESULT __stdcall CAnimalRoot::CreateInstance(IN OPTIONAL IUnknown* pUnkOuter, IN REFIID riid, OUT void** ppvObject)
{
    return S_OK;
}

HRESULT __stdcall CAnimalRoot::LockServer(IN BOOL fLock)
{
    return S_OK;
}

HRESULT __stdcall CAnimalRoot::ParseDisplayName(
    IN IBindCtx* pbc, 
    IN LPOLESTR pszDisplayName, 
    OUT ULONG* pchEaten, 
    OUT IMoniker** ppmkOut
)
{
    HRESULT hResult;
    size_t stLen = _tcslen(pszDisplayName) + 1;
    LPOLESTR lpString;
    ULONG Eaten = 0;

    lpString = new OLECHAR[stLen];
    if (!lpString) return S_FALSE;
    RtlZeroMemory(lpString, stLen * sizeof(OLECHAR));
    RtlCopyMemory(lpString, pszDisplayName, stLen * sizeof(OLECHAR));

    for (unsigned int i = 1; i < stLen; i++)
    {
        if (lpString[i] != OLESTR('!'))
        {
            Eaten++;
        }
        else
        {
            lpString[i] = OLESTR('\0');
            break;
        }
    }

    if (Eaten)
        Eaten++;

    hResult = CreateItemMoniker(OLESTR("!"), lpString + 1, ppmkOut);
    if (pchEaten)
    {
        if (hResult == S_OK)
        {
            *pchEaten = Eaten;
        }
        else
        {
            *pchEaten = 0;
        }
    }

    if (lpString)
        delete[] lpString;

    return hResult;
}

HRESULT __stdcall CAnimalRoot::EnumObjects(IN DWORD grfFlags, OUT IEnumUnknown** ppenum)
{
    return S_OK;
}

HRESULT __stdcall CAnimalRoot::LockContainer(IN BOOL fLock)
{
    return S_OK;
}

HRESULT __stdcall CAnimalRoot::GetObject(
    IN LPOLESTR pszItem, 
    IN DWORD dwSpeedNeeded, 
    IN IBindCtx* pbc, 
    IN REFIID riid, 
    OUT void** ppvObject
)
{
    IUnknown* lpIUnknown;
    ISetName* lpISetName;

    HRESULT hResult = CoCreateInstance(CLSID_ANIMALS, nullptr, CLSCTX_INPROC_SERVER, riid, ppvObject);
    if (hResult == S_OK)
    {
        lpIUnknown = (IUnknown*)*ppvObject;
        lpIUnknown->QueryInterface(IID_ISetName, (void**)&lpISetName);
        lpISetName->SetName(pszItem);
        lpISetName->Release();
    }
    return hResult;
}

HRESULT __stdcall CAnimalRoot::GetObjectStorage(
    IN LPOLESTR pszItem, 
    IN IBindCtx* pbc, 
    IN REFIID riid, 
    OUT void** ppvStorage
)
{
    return S_OK;
}

HRESULT __stdcall CAnimalRoot::IsRunning(IN LPOLESTR pszItem)
{
    return S_OK;
}


